﻿#include "../../beans/headers/FlowBlockItem.h"
#include "../../views/headers/ItemScene.h"
#include <QDebug>

FlowBlockItem::FlowBlockItem(int itemTypeID,int flowBlockItemID,QGraphicsItem *parent):
    QGraphicsItem(parent),
    m_FlowBlockItemID(flowBlockItemID),
    m_ItemTypeID(itemTypeID),
    m_Width(100),
    m_Height(50)
{
    initial();
}

FlowBlockItem::~FlowBlockItem()
{

}

// 辅助函数（初始化设置）
void FlowBlockItem::initial(void)
{
    // 设置BlockItem可以被选中和移动
    this->setFlags(QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemIsMovable);

    // 设置BlockItem接受悬停事件
    this->setAcceptHoverEvents(true);

    // 接受鼠标按键
    this->setAcceptedMouseButtons(Qt::LeftButton);

    // 设置十字符号鼠标指针画面
    this->setCursor(Qt::SizeAllCursor);

    // 设置部件是否透明（1.0表示100%不透明）
    this->setOpacity(1.0);

    // 设置存储位
    this->setData(ITEM_CLASS,EnumType::ItemClass_FLowBlockItem);
}

// 鼠标双击事件
void FlowBlockItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
{
    QList<QGraphicsItem *> childItems_List = this->childItems();
    TextItem *textItem;

    // childItems_List肯定不为空
    foreach (QGraphicsItem *item, childItems_List)
    {

        if(item->data(ITEM_CLASS).toInt() == EnumType::ItemClass_TextItem)
        {
            textItem = qgraphicsitem_cast<TextItem *>(item);
        }
    }

    ItemScene *scene = qobject_cast<ItemScene *>(this->scene());

    scene->setFocusItem(textItem);

    // 双击BlockItem就将内部的TextItem设置为焦点项
    textItem->setTextInteractionFlags(Qt::TextEditorInteraction);
    textItem->setFocus();

    QGraphicsItem::mouseDoubleClickEvent(event);
}

// 继承重写函数（所有绘制都要被限制在这个矩形内部）
QRectF FlowBlockItem::boundingRect(void) const
{
    // 设置Item的矩形默认大小
    return QRectF(0,0,m_Width,m_Height);
}

//  继承重写函数（绘画Item）
void FlowBlockItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    // 用于检测是否被选中的判断对象
    QStyleOptionGraphicsItem qsgi;

    //  （BlockItem是否被选中）、(是否有其它Item或鼠标与之碰撞)、（场景中实时光标坐标是否在BlockItem上）
    bool blockItemIsSelected,iscolliding,isContainsPos;

    // 当前BlockItem的宽和高
    qreal width,height;

    ItemScene *itemScene;
    QPointF sceneCursorPos ;

    qsgi.initFrom(widget);

    // 碰撞检测(如果跟其他BlockItem碰撞了，包括LineItem)
    iscolliding = this->thisCollidingItems();

    width = this->m_Width;
    height = this->m_Height;
    blockItemIsSelected = option->state & QStyle::State_Selected;
    itemScene = qobject_cast<ItemScene *>(this->scene());
    sceneCursorPos = itemScene->getCursorPos();

    // 检测场景中的实时鼠标坐标是不是在BlockItem上
    isContainsPos =  this->containSceneCursorPos(sceneCursorPos);

    // 如果BlockItem被选中，就显示SquareItem和DotItem
    emit signal_BlockItemIsSelected(blockItemIsSelected);

    // 如果被选中
    // 选中时绘制虚线框（option->state中包含有State_Selected）【State_Enabled|State_Sunken|State_Selected】
    if (blockItemIsSelected)
    {
        qsgi.state = QStyle::State_None;

        // 边框颜色
        QColor color = QColor(Qt::red);

        // 画boundingRect虚线框
        painter->setPen(QPen(QColor(Qt::black),1,Qt::DotLine));
        painter->drawRect(this->boundingRect());

        // 绘制虚线
        painter->setPen(QPen(color,2,Qt::DashLine));
        painter->setBrush(Qt::NoBrush);

    }

    // 没有被选中时
    else
    {
        QPen pen = (iscolliding ? QPen(Qt::yellow,1) : QPen(Qt::black,1));

        painter->setPen(pen);

        // 只要鼠标在BlockItem上就DotItem（如果BlockItem被选中不能不显示）
        emit signal_BlockItemContainsSceneCursorPos(isContainsPos);
    }



    // 绘画图形，坐标一切坐标都相对与boundingRect()的矩形内部绘画
    // 如果是菱形（Diamond）
    if(m_ItemTypeID == EnumType::BlockItemType_Diamond){

        QPointF topPoint(width / 2 ,0 );
        QPointF leftPoint(0 ,height / 2 );
        QPointF bottomPoint(width / 2 ,height );
        QPointF rightPoint(width ,  height / 2);

        painter->drawLine(topPoint,leftPoint);
        painter->drawLine(leftPoint,bottomPoint);
        painter->drawLine(bottomPoint,rightPoint);
        painter->drawLine(rightPoint,topPoint);

        // 如果是圆角矩形（四个角有弧度）
    }else if(this->m_ItemTypeID == EnumType::BlockItemType_Rect)
    {
        // 前面是个参数是矩形的坐标和宽高，后面两个是角的X方向和y方向的圆滑程度
        painter->drawRoundedRect(0,0,width,height,15,15);

        // 如果是圆弧矩形（左右两边是半圆形）
    }else if(this->m_ItemTypeID == EnumType::BlockItemType_ArcRect)
    {
        QPointF point1,point2,point3,point4;

        // 是个参数分别指：前四个参数在此Item的BoundRect的小矩形的坐标（相对于此Item而言）和宽高，后两个参数：第一个指开始从哪个度数开始，第二个指旋转多少度
        if(width >= height)
        {
            painter->drawArc(0,0,height,height,90 * 16,180 * 16);
            painter->drawArc(width - height,0,height,height,90 * 16,-180 * 16);

            point1 = QPointF(height / 2,0);
            point2 = QPointF(width - height / 2,0);
            point3 = QPointF(height / 2,height);
            point4 = QPointF(width - height / 2,height);
        }else
        {
            painter->drawArc(0,0,2*width / 3,height,90 * 16,180 * 16);
            painter->drawArc(width / 3,0,2*width / 3,height,90 * 16,-180 * 16);

            point1 = QPointF(width / 3,0);
            point2 = QPointF(2*width / 3,0);
            point3 = QPointF(width / 3,height);
            point4 = QPointF(2*width / 3,height);
        }

        painter->drawLine(point1,point2);
        painter->drawLine(point3,point4);

    }else if(this->m_ItemTypeID == EnumType::BlockItemType_Parallelogram)
    {
        QVector<QPointF> points;

        // 左上角
        points.append(QPointF(height,0));

        // 左下角
        points.append(QPointF(0,height));

        // 右下角
        points.append(QPointF(width - height,height));

        // 右上角
        points.append(QPointF(width,0));

        QPolygonF polygon(points);
        painter->drawPolygon(polygon);
    }

    // 设置背景画刷
    painter->setBrush(Qt::white);

    // 便于查看path
    // painter->drawPath(this->shape());

}

// 继承重写函数（碰撞检测的轮廓：被contains() 和 collidesWithPath()使用）
QPainterPath FlowBlockItem::shape(void) const
{
    QPainterPath path;

    qreal width = this->boundingRect().width();
    qreal height = this->boundingRect().height();

    // 如果类型是菱形（Diamond）
    if(m_ItemTypeID == EnumType::BlockItemType_Diamond)
    {
        QPolygonF polygon;
        QVector<QPointF> vecPointFs;

        QPointF topPoint(width / 2 ,0 );
        QPointF leftPoint(0 ,height / 2 );
        QPointF bottomPoint(width / 2 ,height );
        QPointF rightPoint(width ,  height / 2);

        vecPointFs.append(topPoint);
        vecPointFs.append(leftPoint);
        vecPointFs.append(bottomPoint);
        vecPointFs.append(rightPoint);
        polygon.append(vecPointFs);
        path.addPolygon(polygon);

        // 如果类型是圆角矩形（四个角有弧度）
    }else if(m_ItemTypeID == EnumType::BlockItemType_Rect)
    {
        path.addRoundedRect(0,0,width,height,15,15);

        // 如果类型是圆弧矩形（左右两边是半圆）
    }else if(this->m_ItemTypeID == EnumType::BlockItemType_ArcRect)
    {
        // 绘画左边的半圆
        if(width >= height)
        {
            // 将画圆弧的起始点启动到相应的位置
            path.moveTo(height / 2,0);

            // 在相对于this->boundingRect中的左上角坐标为(0,0)，宽高为height中的矩形中，一初始90方向（竖直向上）逆时针扫画180度的弧线
            path.arcTo(0,0,height,height,90.0f,180.0f);
        }
        else
        {
            // 将画圆弧的起始点启动到相应的位置
            path.moveTo(width / 3,0);

            // 在相对于this->boundingRect中的左上角坐标为(0,0)，宽高为height中的矩形中，一初始90方向（竖直向上）逆时针扫画180度的弧线
            path.arcTo(0,0,2*width / 3,height,90.0f,180.0f);
        }


        // 封闭图形，将起始点和末尾点链接
        path.closeSubpath();

        // 绘画右边的半圆形
        if(width >= height)
        {
            // 将其实绘画点移到指定位置
            path.moveTo(width - height / 2,0);

            // 在相对于this->boundingRect中的左上角坐标为(0,0)，宽高为height中的矩形中，一初始90方向（竖直向上）逆时针扫画180度的弧线
            path.arcTo(width - height,0,height,height,90.0f,-180.0f);
        }
        else
        {
            // 将其实绘画点移到指定位置
            path.moveTo(2*width / 3,0);

            // 在相对于this->boundingRect中的左上角坐标为(0,0)，宽高为height中的矩形中，一初始90方向（竖直向上）逆时针扫画180度的弧线
            path.arcTo(width / 3,0,2*width / 3,height,90.0f,-180.0f);
        }

        path.closeSubpath();

        // 绘画中间的矩形
        if(width >= height)
            path.addRect(height / 2,0, width - height,height);
        else
            path.addRect(width / 3,0, width / 3,height);

        // 平行四边形
    }else if(this->m_ItemTypeID == EnumType::BlockItemType_Parallelogram)
    {
        QVector<QPointF> points;

        // 左上角
        points.append(QPointF(height,0));

        // 左下角
        points.append(QPointF(0,height));

        // 右下角
        points.append(QPointF(width - height,height));

        // 右上角
        points.append(QPointF(width,0));

        QPolygonF polygon(points);
        path.addPolygon(polygon);
    }

    // 如果Item是被选中状态，就绘画四个角的path(这是为了能被鼠标箭头碰撞,这样在缩放时依然显示缩放矩形和端口圆点)
    if (this->isSelected())
    {
        // 画是个角的小正方形
        qreal squareWidth = (SquareItem::SQUAREITEM_WIDTH) ;

        path.addRect(0.0 ,0.0 ,squareWidth ,squareWidth );
        path.addRect(0.0 ,this->m_Height - squareWidth,squareWidth ,squareWidth);
        path.addRect(this->m_Width - squareWidth,this->m_Height - squareWidth,squareWidth ,squareWidth );
        path.addRect(this->m_Width - squareWidth,0.0 ,squareWidth ,squareWidth );
    }

    return path;

}

// 辅助函数（碰撞检测，只用父级别和父级别有重叠部分才会返回true）
bool FlowBlockItem::thisCollidingItems(void)
{
    // 获取所有碰撞检测到的QGraphicsItem（包括自己的子Item）
    QList<QGraphicsItem *> QGraphicsItemList = this->collidingItems();

    foreach(QGraphicsItem *tempItem, QGraphicsItemList)
    {
        // 如果子Item的父BlockItem不是此BlockItem，且为空(不加为空判断会问题)
        if(tempItem->data(ITEM_CLASS).toInt() == EnumType::ItemClass_FLowBlockItem)
        {
            return true;
        }
    }

    return false;
}


// 辅助函数（场景中的光标坐标是否在此BlockItem上）
bool FlowBlockItem::containSceneCursorPos(QPointF cursotPos)
{
    QPointF blcokItemScenePos,expandBlcokScenePos,expandBlcokSceneEndPos;
    qreal blockItemWidth,blockItemHeight,expandBlockItemWidth,expandBlockItemHeight;


    blcokItemScenePos = this->scenePos();
    blockItemWidth = this->boundingRect().width();
    blockItemHeight = this->boundingRect().height();

    expandBlcokScenePos = blcokItemScenePos - QPointF(DotItem::DOTITEM_WIDTH / 2,DotItem::DOTITEM_WIDTH / 2);
    expandBlockItemWidth = blockItemWidth + DotItem::DOTITEM_WIDTH;
    expandBlockItemHeight = blockItemHeight + DotItem::DOTITEM_WIDTH;

    expandBlcokSceneEndPos  = expandBlcokScenePos + QPointF(expandBlockItemWidth,expandBlockItemHeight);

    QRectF rect(blcokItemScenePos,expandBlcokSceneEndPos);

    if(rect.contains(cursotPos))
        return true;
    else
        return false;



}


int FlowBlockItem::ItemTypeID() const
{
    return m_ItemTypeID;
}

void FlowBlockItem::setItemTypeID(int ItemTypeID)
{
    m_ItemTypeID = ItemTypeID;
}

int FlowBlockItem::FlowBlockItemID() const
{
    return m_FlowBlockItemID;
}

void FlowBlockItem::setFlowBlockItemID(int FlowBlockItemID)
{
    m_FlowBlockItemID = FlowBlockItemID;
}

void FlowBlockItem::setHeight(const qreal &Height)
{
    m_Height = Height;
}

void FlowBlockItem::setWidth(const qreal &Width)
{
    m_Width = Width;
}
